﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SubhadraSolutions.Sharp.Utils.Collections.Generic
{
    using System.Collections.Specialized;
    using System.ComponentModel;

    using SubhadraSolutions.Sharp.Utils.Algorithms;
    using SubhadraSolutions.Sharp.Utils.Validation;

    public class NotifyCollectionChangedWrapper<T> : AbstractCollectionDecorator<T>, INotifyCollectionChanged, INotifyPropertyChanged where T:new()
    {
        #region Fields

        /// <summary>
        /// The sync lock.
        /// </summary>
        private readonly object syncLock = new object();

        /// <summary>
        /// The is sorted.
        /// </summary>
        private bool isSorted;

        /// <summary>
        /// The sort direction.
        /// </summary>
        private ListSortDirection sortDirection;

        /// <summary>
        /// The sort property.
        /// </summary>
        private PropertyDescriptor sortProperty;

        private readonly IList<T> adaptedList; 

        #endregion Fields

        public event ListChangedEventHandler ListChanged;
        public NotifyCollectionChangedWrapper(IList<T> adaptedObject)
            : base(adaptedObject)
        {
            this.adaptedList = adaptedObject;
        }

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        #region Public Properties

        /// <summary>
        /// Gets a value indicating whether allow edit.
        /// </summary>
        public bool AllowEdit
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether allow new.
        /// </summary>
        public bool AllowNew
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether allow remove.
        /// </summary>
        public bool AllowRemove
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether is sorted.
        /// </summary>
        public bool IsSorted
        {
            get
            {
                return this.isSorted;
            }
        }

        /// <summary>
        /// Gets the sort direction.
        /// </summary>
        public ListSortDirection SortDirection
        {
            get
            {
                return this.sortDirection;
            }
        }

        /// <summary>
        /// Gets the sort property.
        /// </summary>
        public PropertyDescriptor SortProperty
        {
            get
            {
                return this.sortProperty;
            }
        }

        /// <summary>
        /// Gets a value indicating whether supports change notification.
        /// </summary>
        public bool SupportsChangeNotification
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether supports searching.
        /// </summary>
        public bool SupportsSearching
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// Gets a value indicating whether supports sorting.
        /// </summary>
        public bool SupportsSorting
        {
            get
            {
                return true;
            }
        }

        #endregion Public Properties

        #region Public Methods and Operators

        /// <summary>
        /// The add index.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        public void AddIndex(PropertyDescriptor property)
        {
        }

        /// <summary>
        /// The add new.
        /// </summary>
        /// <returns>
        /// The <see cref="object"/>.
        /// </returns>
        public object AddNew()
        {
            int index;
            var t = new T();
            lock (this.syncLock)
            {
                this.Add(t);
                index = this.Count - 1;
            }

            this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, t));
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, index));
            return t;
        }

        /// <summary>
        /// The apply sort.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        /// <param name="direction">
        /// The direction.
        /// </param>
        public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
        {
            var pc = new PropertyComparer<T>(property, direction);
            InPlaceMergeSortHelper.MergeSort(this.adaptedList, pc);
            this.isSorted = true;
            this.sortProperty = property;
            this.sortDirection = direction;

            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        /// <summary>
        /// The find.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        /// <param name="key">
        /// The key.
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        public int Find(PropertyDescriptor property, object key)
        {
            // Specify search columns
            if (property == null)
            {
                return -1;
            }

            for (int i = 0; i < this.adaptedList.Count; i++)
            {
                T item = this.adaptedList[i];
                if (item != null)
                {
                    // Test column search value
                    var value = (string)property.GetValue(item);

                    // If value is the search value, return the
                    // index of the data item
                    if ((string)key == value)
                    {
                        return i;
                    }
                }
            }

            return -1;
        }

        /// <summary>
        /// The remove index.
        /// </summary>
        /// <param name="property">
        /// The property.
        /// </param>
        public void RemoveIndex(PropertyDescriptor property)
        {
        }

        /// <summary>
        /// The remove sort.
        /// </summary>
        public void RemoveSort()
        {
            this.isSorted = false;
            this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
        }

        #endregion Public Methods and Operators

         #region Methods

        /// <summary>
        /// The on list changed.
        /// </summary>
        /// <param name="e">
        /// The e.
        /// </param>
        protected virtual void OnListChanged(ListChangedEventArgs e)
        {
            if (this.ListChanged != null)
            {
                this.ListChanged(this, e);
            }
        }

        #endregion Methods

        public event PropertyChangedEventHandler PropertyChanged;
    }
}